<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script type="importmap">
    {
        "imports": {
            "three": "../node_modules/three/build/three.module.js"
        }
    }
</script>
  <script type="module">
    import * as THREE from "../node_modules/three/build/three.module.js"
    import { OrbitControls } from "../node_modules/three/examples/jsm/controls/OrbitControls.js"

    let scene = null, camera = null, renderer = null, controls = null, light = null, group = null;

    // 建筑长宽
    const baseWidth = 40;
    const baseLength = 60;

    // 初始化场景
    const initScene = () => {
      scene = new THREE.Scene()
      // scene.fog = new THREE.Fog(scene.background, 3000, 5000);
    }

    // 初始化相机
    const initCamera = () => {
      camera = new THREE.PerspectiveCamera(125, window.innerWidth / window.innerHeight, 1, 2000);
      camera.position.set(-30, 30, 50);
    }

    // 初始化灯光
    const initLight = () => {
      light = new THREE.AmbientLight(0xffffff);
      scene.add(light);
    }

    // 初始化渲染器
    const initRenderer = () => {
      renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setClearColor(0x4682B4, 1.0);
      document.body.appendChild(renderer.domElement);
    }

    // 创建组
    const createGroup = () => {
      group = new THREE.Group()
      scene.add(group)
    }

    // 创建地面
    const createGround = () => {
      // 创建贴图
      const groundTexture = new THREE.TextureLoader().load("../image/贴图素材/5.jpg")
      groundTexture.wrapS = groundTexture.wrapT = THREE.ReqeatWrapping;
      // 设置纹理重复(平铺数量)
      groundTexture.repeat.set(20, 25);
      const ground = new THREE.CircleGeometry(500, 100);
      const groundMaterial = new THREE.MeshLambertMaterial({
        side: THREE.DoubleSide,
        map: groundTexture,
      })
      const groundMesh = new THREE.Mesh(ground, groundMaterial);
      groundMesh.name = "地面";
      groundMesh.rotateX(-Math.PI / 2);
      scene.add(groundMesh);
    }

    // 创建地板
    const createFloor = () => {
      const texture = new THREE.TextureLoader().load("../image/贴图素材/木头材质.png")
      // 设置地板大小，由于后面将要生成墙体存在设置为1的厚度，因此这里对地板的x,z均-2
      const floor = new THREE.BoxGeometry(baseWidth - 2, 1, baseLength - 2)
      const material = new THREE.MeshPhongMaterial({ map: texture });
      const mesh = new THREE.Mesh(floor, material);
      mesh.position.set(0, 1, 0);
      mesh.name = "地板"
      group.add(mesh)
    }

    // 创建墙体
    const createWall = () => {
      const wallTexture = new THREE.TextureLoader().load("../image/贴图素材/大理石.jpg")
      const wall = new THREE.BoxGeometry(baseLength, 20, 1);
      const wallMaterial = new THREE.MeshPhongMaterial({ map: wallTexture });
      // 墙体的网格
      const wallMesh = new THREE.Mesh(wall, wallMaterial);
      return wallMesh
    }

    // 创建左右墙体添加到组中
    const createWallMesh = () => {
      const leftWall = createWall();
      leftWall.rotateY(Math.PI / 2)
      leftWall.position.set(-baseWidth / 2, 10, 0)
      leftWall.name = "左侧的墙"
      group.add(leftWall)

      const rightWall = leftWall.clone();
      rightWall.position.set(baseWidth / 2, 10, 0);
      rightWall.name = "右侧的墙"
      group.add(rightWall)
    }

    // 生成不规则墙体的形状
    function genwallShape() {
      const shape = new THREE.Shape();
      let height = 20;//墙的高度
      shape.moveTo(0, 0); //起点
      shape.lineTo(0, height); //墙体高度
      shape.lineTo(baseWidth / 2 - 1, height + 5); //墙体顶点
      shape.lineTo(baseWidth / 2 - 1, height + 6); //墙体顶点
      shape.lineTo(baseWidth / 2 + 1, height + 6); //墙体顶点
      shape.lineTo(baseWidth / 2 + 1, height + 5); //墙体顶点
      shape.lineTo(baseWidth, height);
      shape.lineTo(baseWidth, 0);
      shape.lineTo(0, 0);
      return { shape };
    }

    //创建不规则墙体
    function createIrregularWall(shape, position) {
      const extrudeSettings = {
        depth: 1,//定义深度，由于挤压几何体的点位都是x，y坐标组成的二位平面，这个参数定义向z轴的延展长度，即为墙的厚度
        bevelEnabled: false,//对挤出的形状应用是否斜角，默认值为true
      };
      const wallTexture = new THREE.TextureLoader().load("../image/贴图素材/大理石.jpg");
      wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
      wallTexture.repeat.set(0.05, 0.05);
      const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
      const material = new THREE.MeshPhongMaterial({ map: wallTexture });
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.set(...position);
      group.add(mesh);
      return mesh;
    }

    //创建不带门的不规则墙体
    function createNoDoorWall() {
      let { shape } = genwallShape();
      let mesh = createIrregularWall(shape, [-baseWidth / 2, 0, -baseLength / 2]);
      mesh.name = "带门的墙对面的墙";
    }

    // 创建带门的不规则墙体
    function createDoorWall() {
      let { shape } = genwallShape();
      const door = new THREE.Path();
      //门的位置
      door.moveTo(baseWidth / 2 + 5, 0);
      door.lineTo(baseWidth / 2 + 5, 16);
      door.lineTo(baseWidth / 2 - 5, 16);
      door.lineTo(baseWidth / 2 - 5, 0);
      door.lineTo(baseWidth / 2 + 5, 0);
      // 形状上的孔洞
      shape.holes.push(door);
      let mesh = createIrregularWall(shape, [
        -baseWidth / 2,
        0,
        baseLength / 2 - 1,
      ]);
      mesh.name = "带门的墙";
    }

    // 创建右屋顶
    function createRightRoof() {
      //用勾股定理求出屋顶宽:Math.sqrt((baseWidth / 2) ** 2 + 5 ** 2)
      let width = Math.sqrt((baseWidth / 2) ** 2 + 5 ** 2) + 5;//+5让有一点屋檐的效果
      const geometry = new THREE.BoxGeometry(baseLength + 2, width, 1);
      const texture = new THREE.TextureLoader().load("../image/贴图素材/木头材质地板.png");
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(2, 2);
      const material = new THREE.MeshPhongMaterial({ map: texture });
      const mesh = new THREE.Mesh(geometry, material);
      mesh.rotateZ(THREE.MathUtils.degToRad(75));
      mesh.rotateY(-Math.PI / 2);
      mesh.position.set(baseWidth / 3 - 1, 22, 0);
      mesh.name = "右屋顶";
      group.add(mesh);
      return { roof: mesh, width };
    }

    // 创建左屋顶
    const createLeftRoof = () => {
      let { roof, width } = createRightRoof();
      let leftRoof = roof.clone();
      leftRoof.rotateX(THREE.MathUtils.degToRad(30));
      leftRoof.position.set(-baseWidth / 3 + 1, 22, 0);
      leftRoof.name = "左屋顶";
      group.add(leftRoof);
    }

    // 创建门
    function createDoor() {
      //纹理贴图
      const texture = new THREE.TextureLoader().load("../image/贴图素材/木头材质地板.png");
      //门的大小、尺寸
      const door = new THREE.BoxGeometry(10, 15, 0.5);
      const material = new THREE.MeshPhongMaterial({
        map: texture,
        transparent: true,
        opacity: 1,
      });
      const doorMesh = new THREE.Mesh(door, material);
      doorMesh.name = "门";
      doorMesh.position.x = 0;
      doorMesh.position.y = 8;
      doorMesh.position.z = baseLength / 2 - 1;
      group.add(doorMesh);
    }

    // 初始化模型
    const initContent = () => {
      createGroup()//组
      createGround()//地面
      createFloor()//底板
      createWallMesh()//墙体
      createNoDoorWall()//不带门的
      createDoorWall()//带门的
      createRightRoof()//右屋顶
      createLeftRoof()//左屋顶
      createDoor()//门
    }

    // 初始化轨迹球控件
    const initControls = () => {
      controls = new OrbitControls(camera, renderer.domElement)
      controls.enableDamping = true
    }


    // 更新控件
    const update = () => [
      controls.update()
    ]

    // 初始化
    const init = () => {
      initScene()//场景
      initCamera()//相机
      initLight()//光源
      initRenderer()//渲染器
      initContent()//模型
      initControls()//控制
      document.addEventListener('resize', onWindowResize)//窗口变化
    }
    // 窗口变动时触发
    const onWindowResize = () => {
      camera.aspect = window.innerWidth / window.innerHeight
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    const animate = () => {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
      update()
    }

    init()
    animate()

  </script>
</body>

</html>